/**********************************************************************
 * esp8266.c
 *Copyright:(C)ZhouYanlin<www.zhouyanlin1222@qq.com>
 *
 *Description: for ESP8266-01s to community
 *
 *
 *  Created on: 2021年6月5日
 *      Author: ZhouYanlin
 **********************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "at_cmd.h"

#define  ESP_PRINT
#ifdef 	 ESP_PRINT
#define esp_printf(format,args...) printf(format, ##args)
#else
#define esp_printf(format,args...) do{} while(0)
#endif

#define  DBG_PRINT
#ifdef 	 DBG_PRINT
#define esp_dbg(format,args...) printf(format, ##args)
#else
#define esp_dbg(format,args...) do{} while(0)
#endif


/* ESP8266 WiFi模块复位重启函数。返回值为0表示成功，!0 表示失败  */
int esp8266_module_reset(void)
{
	HAL_Delay(500);
	return send_atcmd("AT+RST\r\n", NULL, 500, NULL, 0);
}


/* ESP8266 WiFi模块初始化函数。返回值为0表示成功，!0 表示失败  */
int esp8266_module_init(void)
{
	int i;
	esp_printf("INFO:The module start to reset now ...\r\n");

	esp8266_module_reset();

	for(i=0; i<6; i++)
	{
		if( send_atcmd_check_module() )
		{
			esp_printf("INFO:Send At to module and got reply ok\r\n");
			break;
		}

		HAL_Delay(100);

	}

	if(i>=6)
	{
		esp_printf("ERROR:Send At to module and got reply fail\r\n");
		return -2;
	}

#if 0
	if( set_atcmd_echo(ECHO_OFF))
	{
		esp_printf("INFO:Set module to close echo and got reply fail\r\n");
		return -3;
	}

	esp_dbg("Set module to close echo and got reply successfully\r\n");
#endif

	if(send_atcmd_check_ok("AT+CWMODE_DEF=1\r\n", 500))
	{
		esp_printf("%s--->ERROR:Set the working mode to station and got reply fail\r\n",__FUNCTION__);
		return -4;

	}

	esp_dbg("INFO:Set the working mode to station and got reply successfully\r\n");

	if(send_atcmd_check_ok("AT+CWDHCP=1,1\r\n", 500))
	{
		esp_printf("%s--->ERROR:Enable ESP8266 Station mode DHCP failure\r\n",__FUNCTION__);
		return -5;
	}

	esp_dbg("INFO:Enable ESP8266 Station mode DHCP successfully\r\n");
	HAL_Delay(500);
	return 0;
}

/* ESP8266 WiFi模块连接路由器函数。返回值为0表示成功，!0 表示失败  */
int esp8266_join_network(char *ssid, char *pwd)
{
	int	i;
	char atcmd_buf[128];

	if(NULL == ssid || NULL == pwd)
	{
		esp_printf("%s-->ERROR:Invalid input arguments\r\n", __FUNCTION__);
		return -1;
	}

	memset(atcmd_buf, 0, sizeof(atcmd_buf));
	snprintf(atcmd_buf, sizeof(atcmd_buf), "AT+CWJAP=\"%s\",\"%s\"\r\n", ssid, pwd);
	if(send_atcmd_check_str(atcmd_buf, "CONNECTED", 5000))
	{
		esp_printf("%s-->ERROR:Connect to %s gateway failure!\r\n", __FUNCTION__, ssid);
		return -2;
	}

	esp_printf("Connect to gateway successfully!\r\n");

	/* check got IP address or not by netmask (255.)*/
	for(i=0; i<10; i++)
	{
		if( !send_atcmd_check_str("AT+CIPSTA_CUR?\r\n", "255.", 1000) )
		{
			esp_printf("INFO: ESP8266 got IP address ok\r\n");
			return 0;
		}

		HAL_Delay(300);
	}

	esp_printf("ERROR: ESP8266 get IP address failure\r\n");
	return -3;

}


/*
 *	Reply for AT+CIPSTA_CUR? :
 * +CIPSTA_CUR:ip:"192.168.2.100"
 * +CIPSTA_CUR:gateway:"192.168.2.1"
 * +CIPSTA_CUR:netmask:"255.255.255.0"
 *
*/
static int util_parser_ipaddr(char *buf, char *key, char *ipaddr, int size)
{
	char        *start;
	char        *end;
	int          len;

	if( !buf || !key || !ipaddr )
	{
		return -1;
	}

	/* find the key string */
	start = strstr(buf, key);
	if( !start )
	{
		return -2;
	}

	start+=strlen(key) + 1;  /* Skip " */
	end = strchr(start, '"');  /* find last " */
	if( !end )
	{
		return -3;
	}

    len = end - start;
    len = len>size ? size : len;

    memset(ipaddr, 0, size);
    strncpy(ipaddr, start, len);

    return 0;
}

/* ESP8266 获取自己的IP地址和网关IP地址。返回值为0表示成功，!0 表示失败  */
int esp8266_get_ipaddr(char *ipaddr, char *gateway, int ipaddr_size)
{
	if( !ipaddr || !gateway || ipaddr_size<7 )
	{
		esp_printf("ERROR: Invalid input arguments\r\n");
		return -1;
	}

	if( send_atcmd_check_str("AT+CIPSTA_CUR?\r\n", "255.", 1000) )
	{
		esp_printf("ERROR: ESP8266 AT+CIPSTA_CUR? command failure\r\n");
		return -2;
	}

	if( util_parser_ipaddr(g_atcmd_rxbuf, "ip:", ipaddr, ipaddr_size) )
	{
		esp_printf("ERROR: ESP8266 AT+CIPSTA_CUR? parser IP address failure\r\n");
		return -3;
	}

	if( util_parser_ipaddr(g_atcmd_rxbuf, "gateway:", gateway, ipaddr_size) )
	{
		esp_printf("ERROR: ESP8266 AT+CIPSTA_CUR? parser gateway failure\r\n");
		return -4;
	}

	esp_dbg("INFO: ESP8266 got IP address[%s] gateway[%s] ok\r\n", ipaddr, gateway);
	return 0;
}

/* ESP8266 WiFi模块做ping命令测试网络连通性。返回值为0表示成功，!0 表示失败  */
int esp8266_ping_test(char *host)
{
		char	atcmd[128];

		if( !host )
		{
			esp_printf("ERROR: Invalid input arguments\r\n");
			return -1;
		}

		memset(atcmd, 0, sizeof(atcmd));
		snprintf(atcmd, sizeof(atcmd), "AT+PING=\"%s\"\r\n", host);
		if( send_atcmd_check_ok(atcmd, 5000) )
		{
			esp_printf("ERROR: ESP8266 ping test [%s] failure\r\n", host);
			return -2;
		}

		esp_printf("INFO: ESP8266 ping test [%s] ok\r\n", host);
		return 0;
}


/* ESP8266 WiFi模块建立TCP socket 连接函数。返回值为0表示成功，!0 表示失败 */
int esp8266_sock_connect(char *servip, int port)
{
	char atcmd[256];

	if(NULL == servip || port <= 0)
	{
		esp_printf("ERROR: Invalid input argument\r\n");
		return -1;
	}

#if 0
	send_atcmd_check_ok("AT+CIPMUX=0\r\n", 1500);	/* 我用ESP8266模块版本（0.7）不支持此命令 */

#endif

	memset(atcmd, 0, sizeof(atcmd));
	snprintf(atcmd, sizeof(atcmd), "AT+CIPSTART=\"TCP\",\"%s\",%d\r\n", servip, port);
	if(send_atcmd(atcmd, "CONNECT", 3000, NULL, 0))
	{
		esp_printf("ERROR: Connect to socket:%s:%d fail\r\n", servip, port);
		return -3;
	}
	HAL_Delay(500);
	if(esp8266_ping_test(servip))
	{
		esp_printf("ERROR: Ping to socket:%s fail\r\n", servip);
		return -4;
	}

	esp_dbg("INFO:Connect to socket:%s:%d successfully\r\n", servip, port);
	return 0;

}

/* ESP8266 WiFi模块断开TCP socket 连接函数。返回值为0表示成功，!0 表示失败 */
int esp8266_sock_disconnect(void)
{
	return send_atcmd_check_ok("AT+CIPCLOSE\r\n", 1000);
}

int atcmd_send_data(unsigned char *data, int bytes, unsigned int timeout)
{
	int                       rv = -1;
	unsigned int              i;

	/* check function input arguments validation */
	if(  NULL == data || bytes <= 0 )
	{
		esp_printf("ERROR: Invalid input arguments\r\n");
		return -1;
	}

	esp_printf("\r\nStart AT command send [%d] bytes data\n", bytes);

	clear_atcmd_rxbuf();
	HAL_UART_Transmit(at_uart, data, bytes, 1000);

	/* Receive AT reply string by UART interrupt handler, stop by "OK/ERROR" or timeout */
	for(i=0; i<timeout; i++)
	{
		if( strstr(g_atcmd_rxbuf, "SEND OK\r\n") )
		{
			rv = 0;
			goto CleanUp;
		}

		if( strstr(g_atcmd_rxbuf, "ERROR\r\n") )
		{
			rv = -2;
			goto CleanUp;
		}

		HAL_Delay(1);
	}


CleanUp:
	esp_printf("<<<< AT command reply:\r\n%s", g_atcmd_rxbuf);
	return rv;
}


/* ESP8266 WiFi通过TCP Socket发送数据函数。返回值为0表示失败，>0 表示成功发送字节数 */
int esp8266_sock_send(unsigned char *data, int bytes)
{
	char           atcmd[128];

	if( !data || bytes<= 0)
	{
		esp_printf("ERROR: Invalid input arguments\r\n");
		return -1;
	}

	memset(atcmd, 0, sizeof(atcmd));
	snprintf(atcmd, sizeof(atcmd), "AT+CIPSEND=%d\r\n", bytes);
	if( send_atcmd(atcmd, ">", 1000, NULL, 0) )
	{
		esp_printf("ERROR: AT+CIPSEND command failure\r\n");
		return -2;
	}

	esp_printf("INFO: AT+CIPSEND command successfully\r\n");

	if( atcmd_send_data((unsigned char *)data, bytes, 1000) )
	{
		esp_printf("ERROR: AT+CIPSEND send data failure\r\n");
		return -3;
	}

	return bytes;
}


/* ESP8266 WiFi通过TCP Socket接收数据函数。返回值为0无数据，>0 表示接收到数据字节数 */
int esp8266_sock_recv(unsigned char *buf, int size)
{
	char                *data = NULL;
	char                *ptr = NULL;

	int                  len;
	int                  rv;
	int                  bytes;

	if( NULL == buf || size<= 0)
	{
		esp_printf("ERROR: Invalid input arguments\r\n");
		return -1;
	}

	if( g_atcmd_bytes<= 0 )
	{
		return 0;
	}

	/* No data arrive or not integrated
	 * reply:	+IPD,5:Hello
	 *
	 * */
	if( !(ptr=strstr(g_atcmd_rxbuf, "+IPD,")) ||  !(data=strchr( g_atcmd_rxbuf, ':' )) )
	{
		return 0;
	}

	data ++;	/* Skip ':' */
	bytes = atoi(ptr+strlen("+IPD,")); /*  "：" 后面的字符串的字节数(接收有效数据的字节数) */

	len = g_atcmd_bytes - (data-g_atcmd_rxbuf);	/* 计算收到有效数据的字节数 */

	if( len < bytes )	/* 接收未完成 */
	{
		esp_printf("+IPD data not receive over, receive again later ...\r\n");
		return 0;
	}

	memset(buf, 0, size);
	rv = bytes>size ? size : bytes;
	memcpy(buf, data, rv);

	clear_atcmd_rxbuf();

	return rv;
}


